package com.yy.QSManage.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.yy.QSManage.dao.FaSysMemberMapper;
import com.yy.QSManage.exception.ServiceException;
import com.yy.QSManage.model.dto.ChangePasswordDto;
import com.yy.QSManage.model.dto.LoginDto;
import com.yy.QSManage.model.dto.LoginResultDto;
import com.yy.QSManage.model.entity.FaSysMember;
import com.yy.QSManage.model.entity.FaSysMenu;
import com.yy.QSManage.model.entity.FaSysRoleMenu;
import com.yy.QSManage.model.entity.gen.FaSysMenuView;
import com.yy.QSManage.service.FaSysMemberRoleService;
import com.yy.QSManage.service.FaSysMemberService;
import com.yy.QSManage.service.FaSysRoleMenuService;
import com.yy.QSManage.service.base.impl.BaseServiceImpl;
import com.yy.QSManage.service.gen.FaSysMenuViewService;
import com.yy.QSManage.tool.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

@Service
public class FaSysMemberServiceImpl extends BaseServiceImpl<FaSysMemberMapper, FaSysMember> implements FaSysMemberService{

    private final String MEMBER_TOKEN = "member_token:{}";

    @Autowired
    FaSysMemberRoleService memberRoleService;
    @Autowired
    FaSysRoleMenuService roleMenuService;
    @Autowired
    FaSysMenuViewService menuViewService;
    @Autowired
    RedisTool redisTool;

    @Override
    public LoginResultDto login(LoginDto loginInfoDto, HttpServletRequest request) {
        // 账号密码解密
        String account = EncryptTool.rsaDecrypt(loginInfoDto.getAccount());
        String password = EncryptTool.rsaDecrypt(loginInfoDto.getPassword());
        // 判断是否存在
        FaSysMember member = getByParam(FaSysMember::getAccount, account);
        if(Objects.isNull(member)){
            throw new ServiceException("账号不存在！");
        }
        // 判断密码是否正确
        String passwordMd5 = SecureUtil.md5(password);
        if(!member.getPassword().equals(passwordMd5)){
            throw new ServiceException("密码错误！");
        }
        // 查询菜单_权限关联信息
        List<FaSysMenuView> menuList = getPower(member);
        // 生成token,将登录信息存到redis
        HashMap<String, Object> map = new HashMap<>();
        member.setPassword(null);
        map.put("member", member);
        Map<String, String> tokenMap = JwtTool.createToken(map);
        // 将加密token的公钥缓存至redis
        String memberToken = StrUtil.format(MEMBER_TOKEN, DigestUtil.md5Hex(tokenMap.get(JwtTool.TOKEN)));
        boolean redisConnect = redisTool.setHours(memberToken, tokenMap.get(JwtTool.PUBLIC_KEY), 3);
        if(!redisConnect){
            throw new ServiceException("redis服务异常");
        }
        // 封装登录返回参数
        LoginResultDto loginResultDto = new LoginResultDto();
        loginResultDto.setMember(member)
                .setToken(tokenMap.get(JwtTool.TOKEN))
                .setMenuTree(menuList);
        // 改变登录状态，登录时间
        loginStatusChange(member.getId(), 2);
        return loginResultDto;
    }

    @Override
    public List<FaSysMenuView> getPower(FaSysMember member) {
        List<FaSysMenuView> faSysMenus = List.of();
        // 查询角色
//        List<FaSysMemberRole> memberRoles = memberRoleService.listByParam(FaSysMemberRole::getMemberId, id);
//        List<String> roleIds = memberRoles.stream().map(FaSysMemberRole::getRoleId).collect(Collectors.toList());

        if(StrUtil.isBlank(member.getRoleIds())){
            return faSysMenus;
        }
        JSONArray roleIdsJson = JSONUtil.parseArray(member.getRoleIds());
        List<FaSysRoleMenu> roleMenuList = roleMenuService.listInParam(FaSysRoleMenu::getRoleId, roleIdsJson);
        // 合并角色权限
        Map<String, FaSysRoleMenu> roleMenuMap = new HashMap<>();
        for (FaSysRoleMenu roleMenu : roleMenuList) {
            FaSysRoleMenu roleMenuItem = roleMenuMap.get(roleMenu.getMenuId());
            if(Objects.isNull(roleMenuItem)){
                roleMenuMap.put(roleMenu.getMenuId(), roleMenu);
            }else{
                if(!"[]".equals(roleMenu.getFunctionRole())){
                    // 合并功能
                    JSONArray functions = JSONUtil.parseArray(roleMenuItem.getFunctionRole());
                    JSONArray functions2 = JSONUtil.parseArray(roleMenu.getFunctionRole());
                    // 合并
                    functions.addAll(functions2);
                    // 去重
                    List<Object> strings = new ArrayList<>(new HashSet<>(functions));
                    String jsonStr = JSONUtil.toJsonStr(strings);
                    roleMenuItem.setFunctionRole(jsonStr);
                }
            }
        }
        // 获取菜单
        Set<String> menuIds = roleMenuMap.keySet();
        if(menuIds.size() == 0){
            return List.of();
        }
        faSysMenus = menuViewService.listInParam(FaSysMenu::getId, menuIds);
        // 合并权限和菜单
        faSysMenus.forEach(faSysMenu -> {
            FaSysRoleMenu roleMenu = roleMenuMap.get(faSysMenu.getId());
            faSysMenu.setFunctionRole(roleMenu.getFunctionRole());
        });
        return TreeUtil.build(faSysMenus);
    }



    @Override
    public void logout(String token) {
        String memberToken = StrUtil.format(MEMBER_TOKEN, DigestUtil.md5Hex(token));
        FaSysMember member = getTokenMember(token);
        // 修改登录状态
        loginStatusChange(member.getId(), 1);
        redisTool.del(memberToken);
    }

    @Override
    public void refresh(String token) {
        String memberToken = StrUtil.format(MEMBER_TOKEN, DigestUtil.md5Hex(token));
        Object publicKey = redisTool.get(memberToken);
        redisTool.setHours(memberToken, publicKey, 3);
    }

    @Override
    public FaSysMember getTokenMember(String token) {
        String memberToken = StrUtil.format(MEMBER_TOKEN, DigestUtil.md5Hex(token));
        // 获取公钥
        String publicKey = redisTool.get(memberToken).toString();
        Object memberObj = JwtTool.getPayload(token, publicKey, "member");
        return JSONUtil.toBean(JSONUtil.parseObj(memberObj), FaSysMember.class);
    }

    @Override
    public FaSysMember getTokenMember() {
        return getTokenMember(RequestBuffUtil.getToken());
    }

    @Override
    public String getTokenMemberId() {
        FaSysMember tokenMember = getTokenMember();
        if(Objects.isNull(tokenMember)){
            return null;
        }
        return tokenMember.getId();
    }


    @Override
    public void changePassword(ChangePasswordDto changePasswordDto) {
        if(StrUtil.isBlank(changePasswordDto.getOldPassword())){
            throw new ServiceException("旧密码未填写");
        }
        if(StrUtil.isBlank(changePasswordDto.getNewPassword())){
            throw new ServiceException("新密码未填写");
        }
        // 获取当前用户信息
        FaSysMember tokenMember = getTokenMember(); // 不包含密码
        FaSysMember member = getById(tokenMember.getId());
        // 判断旧密码是否正确
        if(!member.getPassword().equals(SecureUtil.md5(changePasswordDto.getOldPassword()))){
            throw new ServiceException("旧密码错误！");
        }
        // 修改密码
        LambdaUpdateWrapper<FaSysMember> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(FaSysMember::getId, member.getId())
                .set(FaSysMember::getPassword, SecureUtil.md5(changePasswordDto.getNewPassword()));
        update(updateWrapper);
    }

    /**
     * 改变用户登录状态
     * @param id
     * @param status
     */
    private void loginStatusChange(String id, Integer status){
        LambdaUpdateWrapper<FaSysMember> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(FaSysMember::getId, id)
                .set(FaSysMember::getStatus, status)
                .set(FaSysMember::getUpdateTime, new Date());
        if(status.equals(2)){
            updateWrapper.set(FaSysMember::getLastIp, RequestBuffUtil.getIp())
                    .set(FaSysMember::getLastTime, new Date());
        }
        update(updateWrapper);
    }

}

